/* 
 * File:   FontManager.cpp
 * Author: Pie
 * 
 * Created on April 16, 2011, 9:43 AM
 */

#include <vector>
#include <boost/tokenizer.hpp>
//#include <stdio.h>
#include "FontManager.h"

#ifdef DEBUG_NEW
#include "debug/debug_new.h"
#endif
FontManager::FontManager() {
    _current = 0;
    if (FT_Init_FreeType(&_library)) {
        //TODO: Handle freetype error
    }

    add_face("arial", "data/fonts/arial.ttf", 18);
    select_face("arial");
}

FontManager::~FontManager() {
    for (unordered_map<string, FontFace*>::iterator it = _faces.begin();
            it != _faces.end(); ++it) {
        delete it->second;
    }
    FT_Done_FreeType(_library);
}

int FontManager::_next_p2(int p) {
    int rval = 1;
    while (rval < p) rval <<= 1;
    return rval;
}

void FontManager::add_face(const string& name, const string& path, int height) {
    FT_Face ft;
    FT_Glyph glyph;

    FontFace* ff = new FontFace(height);

    FT_New_Face(_library, path.c_str(), 0, &ft);
    FT_Set_Char_Size(ft, 0, height * 64, 0, 0);

    for (int ch = 0; ch < 128; ++ch) {

        //printf("%d is cool\n", ch);
        
        FT_Load_Char(ft, ch, FT_LOAD_DEFAULT);
        FT_Get_Glyph(ft->glyph, &glyph);
        FT_Glyph_To_Bitmap(&glyph, ft_render_mode_normal, 0, 1);
        FT_BitmapGlyph bglyph = (FT_BitmapGlyph) glyph;
        FT_Bitmap& bitmap = bglyph->bitmap;

        int hbx = ft->glyph->metrics.horiBearingX >> 6;
        int hby = ft->glyph->metrics.horiBearingY >> 6;
        int hh = ft->glyph->metrics.height >> 6;
        int hw = ft->glyph->metrics.width >> 6;
        float adv_x = ft->glyph->advance.x >> 6;
        ff->set_width(ch, adv_x);

        int hr = height;//ft->bbox.yMax / 64 + 1;
        //std::cout << hby << "\n";

        int bw = bitmap.width;
        int br = bitmap.rows;

        int w = _next_p2(bw);
        int h = _next_p2(br);

        GLubyte* exp = new GLubyte[2 * w * h];

        for (int j= 0; j < h; ++j) {
            for (int i = 0; i < w; ++i) {
                int k = 2 * (i + j * w);
                exp[k] = exp[k + 1] =
                        (i >= bw || j >= br) ?
                        0 : bitmap.buffer[i + bw * j];
            }
        }

        glBindTexture(GL_TEXTURE_2D, ff->texture(ch));
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0,
            GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, exp);

        delete [] exp;

        float x = (float) bw / (float) w;
        float y = (float) br / (float) h;


        glNewList(ff->list(ch), GL_COMPILE);

        glBindTexture(GL_TEXTURE_2D, ff->texture(ch));

        glPushMatrix();
        glTranslatef(hbx, -hby + hr, 0);

        glBegin(GL_QUADS);

        glTexCoord2d(0, 0);
        glVertex2f(0, 0);
        glTexCoord2d(0, y);
        glVertex2f(0, hh);
        glTexCoord2d(x, y);
        glVertex2f(hw, hh);
        glTexCoord2d(x, 0);
        glVertex2f(hw, 0);

        glEnd();

        glPopMatrix();
        glTranslatef(adv_x, 0, 0);
        glEndList();
    }
    _faces[name] = ff;
    FT_Done_Face(ft);
}

void FontManager::select_face(const string& name) {
    _current = _faces[name];
}

void FontManager::fit_text(const string& text, string& out, float fit) {
    _current->str_to_width(text, out, fit);
    //return text;
}


void FontManager::render(const string& text, int x, int y, float zindex, float* mw, float* mh) {
    GLuint font = _current->list(0);

    float h = _current->h();
    vector<string> lines;
    char_separator<char> sep("\n");
    tokenizer<char_separator<char> > tokens(text, sep);
    for (tokenizer<char_separator<char> >::iterator it = tokens.begin();
            it != tokens.end(); ++it) {
        lines.push_back(*it);
    }

    glListBase(font);
    glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TRANSFORM_BIT);
    glMatrixMode(GL_MODELVIEW);
    glDisable(GL_LIGHTING);
    glEnable(GL_TEXTURE_2D);
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    float modelview[16];
    glGetFloatv(GL_MODELVIEW_MATRIX, modelview);

    *mw = 0.0f;

    for (size_t i = 0; i < lines.size(); ++i) {
        glPushMatrix();
        glLoadIdentity();
        glTranslatef(x, y + h*i, zindex);
        glMultMatrixf(modelview);

        glCallLists(lines[i].length(), GL_UNSIGNED_BYTE, lines[i].c_str());
        glPopMatrix();

        float nw = _current->get_str_width(lines[i]);
        if (nw > *mw) *mw = nw;
    }
    glPopAttrib();

    *mh = h * lines.size();
}

void FontManager::render(const string& text, int x, int y, int width, int height, float zindex, GUI_ALIGN align, GUI_VALIGN valign, float* mw, float* mh) {
    GLuint font = _current->list(0);

    string mtext;
    fit_text(text, mtext, width);
    float h = _current->h();
    vector<string> lines;
    char_separator<char> sep("\n");
    tokenizer<char_separator<char> > tokens(mtext, sep);
    for (tokenizer<char_separator<char> >::iterator it = tokens.begin();
            it != tokens.end(); ++it) {
        lines.push_back(*it);
    }

    glListBase(font);
    glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TRANSFORM_BIT);
    glMatrixMode(GL_MODELVIEW);
    glDisable(GL_LIGHTING);
    glEnable(GL_TEXTURE_2D);
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    float modelview[16];
    glGetFloatv(GL_MODELVIEW_MATRIX, modelview);

    *mw = 0.0f;

    float ny = y;
    if (valign == GUI_VALIGN_CENTER) {
        ny = y + (height / 2) - ((h*lines.size())/2);
    } else if (valign == GUI_VALIGN_BOTTOM) {
        ny = y + height - (h*lines.size());
    }

    for (size_t i = 0; i < lines.size(); ++i) {
        float nw = _current->get_str_width(lines[i]);
        if (nw > *mw) *mw = nw;

        float nx = x;
        if (align == GUI_ALIGN_CENTER) {
            nx = x + ((width - nw) / 2);
        } else if (align == GUI_ALIGN_RIGHT) {
            nx = x + (width - nw);
        }

        glPushMatrix();
        glLoadIdentity();
        glMultMatrixf(modelview);
        glTranslatef(nx, ny + h*i, zindex);

        glCallLists(lines[i].length(), GL_UNSIGNED_BYTE, lines[i].c_str());
        glPopMatrix();

    }
    glPopAttrib();

    *mh = h * lines.size();
}